#include"apue.h"
#include"ftp.h"

#define bugs 1
/*
 *	功能：创建服务器和客户端连接的套接字
 *	参数：默认或者自定义的端口号
 *	返回值：成功=返回套接字 失败返回-1
 * */
extern char Work_home[512];

int init_sock(int port)
{
	
	int sock_fd;
	struct sockaddr_in addr;
	int ret;

	sock_fd=socket(AF_INET,SOCK_DGRAM,0);
	if(sock_fd<0)
	{
		perror("socket");
	}
	bzero(&addr,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(port);
	addr.sin_addr.s_addr=htonl(INADDR_ANY);
	ret=bind(sock_fd,(struct sockaddr *)&addr,sizeof(addr));
	if(ret<0)
	{
		perror("bind");
		return -1;
	}
	return sock_fd;
}
/*
 *	功能：创建无名管道
 *		  创建共享内存
 *		  创建消息队列
 *	参数：无名管道读写端的地址
 *	返回值：成功带回无名管道的读端写端 返回0 失败返回-1
 * */
int init_ProcSrc(int *pipefd)
{
	int msgid;
	int ret;
	int i;
	int init_shmid;

	bzero(Work_home,sizeof(Work_home));
	//创建管道	
	ret=pipe(pipefd);
	if(ret<0)
	{
		perror("pipe");
		return -1;
	}
/*	//删除共享内存
	init_shmid=shmget(SHMNAME,10*sizeof(struct cli_to_login),IPC_CREAT|O_RDWR);
	if(shmctl(init_shmid,IPC_RMID,NULL))	
	{
		perror("shmctl shmid");
	}
*/	//创建共享内存
	struct cli_to_login *table;
	init_shmid=shmget(SHMNAME,10*sizeof(struct cli_to_login),IPC_CREAT|O_RDWR);
	if(init_shmid<0)
	{
		perror("shmget init");
		return -1;
	}
	table=shmat(init_shmid,NULL,0);
	for(i=0;i<10;i++)
	{
		table[i].cli_addr=(in_addr_t)-1;
		table[i].child_pid=(in_addr_t)-1;
	}
	shmdt(table);
/*	//删除消息队列
	msgid=msgget(MSGNAME,IPC_CREAT);
	if(msgctl(msgid,IPC_RMID,NULL)==-1)
	{
		perror("msgctl msgid");
	}
*/	//创建消息队列
	msgid=msgget(MSGNAME,IPC_CREAT);
	if(msgid<0)
	{
		perror("msgget creat");
		return -1;
	}
	return 0;
}
//******************************************************************//
//处理进程
/*	
 *	功能：处理进程 从管道接受数据
 *		  查看用户是否登录过 没有登录过创建子进程为用户服务
 *		  将数据从处理进程通过消息队列发送到处理子进程
 *		  处理子进程从消息队列接受数据处理后通过网络发回客户端
 *	参数：管道的读端描述符 网络套接字
 *	返回值：无
 * */
void optProc_fun(int pipeRd,int sockfd)
{
	struct cli_to_login *table;
	int cli_index;
	int shmfd;
//	int msgid;
	int ret;
	pid_t child_pid;
	struct revc_to_opt recvProc2OptProc;
	struct msgbuf msg_buf;
	
	shmfd=optProc_shmOpt(SHMNAME);
	if(shmfd<0)
	{
		perror("shmfd");
	}
	
	//打开用户登陆表
	table=shmat(shmfd,NULL,0);
	struct ser_to_cli log;
	int msgid;
	msgid=msgget(MSGNAME,O_RDWR);
	perror("msgget 处理进程");

	while(1)
	{
		bzero(&recvProc2OptProc,sizeof(struct revc_to_opt));
		//从管道中将父进程发送的数据接受
		read(pipeRd,&recvProc2OptProc,sizeof(struct revc_to_opt));

		printf("%s:[%s]type=%d,arg=%s\n",
			__FUNCTION__,
			inet_ntoa(recvProc2OptProc.cli_addr.sin_addr),
			recvProc2OptProc.data.ftm_cmd,
			recvProc2OptProc.data.ftp_arg.arg);

		//创建用户登陆表
			//判断客户是否登陆过，将收受数据中的ip地址和登陆用户表中的信息比对
			
		ret=isCliLogin(table,recvProc2OptProc.cli_addr.sin_addr.s_addr,&cli_index);
		if(ret==FULL)
		{	

			bzero(&log,sizeof(struct ser_to_cli));
			log.result=FAIL;
			strcpy(log.string_result,"服务器已满!");
			sendto(sockfd,&log,sizeof(struct msgbuf),0,
					(struct sockaddr *)&recvProc2OptProc.cli_addr,sizeof(struct sockaddr_in));

			continue;
		}
		if(ret ==NOEXIST)//客户端从未登录过
		{	
			child_pid=fork();//处理进程创建子进程
			if(child_pid<0)
			{
				perror("fork_child");
				continue;
			}
			if(child_pid==0)//处理命令的子进程
			{
				void *(*cmd_opt[])(void *,int)=
				{
					ser_login,
					ser_pwd,
					ser_cd,
					ser_ls,
					ser_get,
					ser_put,
					ser_quit,
				};
				int msgid;
				msgid=msgget(MSGNAME,O_RDWR);

				while(1)
				{	
					bzero(&msg_buf,sizeof(struct msgbuf));
					//从消息队列中接受处理进程发过来的数据
					revc_child_from_optProc(msgid, getpid(),&msg_buf);

						//处理命令
					cmd_opt[msg_buf.mtext.data.ftm_cmd](&msg_buf.mtext,sockfd);

					//向客户端发送命令的结果
				}
			}else
			{

				//处理进程 当客户端从未登录过时记录客户端的登陆信息到用户登陆列表中
				add_cli_to_login_table(table,recvProc2OptProc.cli_addr.sin_addr.s_addr,child_pid,cli_index);
//				printf("[%p]:%d\n",table[0].cli_addr,table[0].child_pid);
			}
		}else
		{
			child_pid=table[cli_index].child_pid;
		}
		//处理进程执行 无论客户端是否以前登录过，都要向子进程发送接受的数据
		send_optProc_to_childs(msgid, recvProc2OptProc,child_pid);
	}
}
//处理进程创建或打开共享内存
/*	
 *	功能：创建或者打开共享内存
 *	参数：共享内存的名称
 *	返回值：共享内存的文件描述符
 * */
int optProc_shmOpt(key_t shmname)
{
	int shmfd;
	shmfd=shmget(SHMNAME,0,O_RDWR);
	if(shmfd<0)
	{
		perror("shmget open");
		return -1;
	}
	return shmfd;
}
/* 
 * 功能：处理子进程从消息队列中读取消息
 * 参数：处理子进程的进程id号
 * 		 接受消息的内存空间地址
 * 返回值：无
 **/
void revc_child_from_optProc(int msgid, pid_t pid,struct msgbuf *msg_buf)
{
	printf("[%s]pid_t=%d\n",__FUNCTION__, pid);
	msgrcv(msgid,msg_buf,sizeof(struct revc_to_opt),(long)pid,0);
	perror("msgrcv 处理子进程");
	
}
//处理进程向消息队列中发送消息
/*	功能：处理进程向消息队列中发送消息
 *	参数：得到的数据
 *		  处理子进程的进程id号
 *	返回值：无
 * */
void send_optProc_to_childs(int msgid, struct revc_to_opt recv_data,pid_t child_pid)
{
	struct msgbuf msg_buf;
	msg_buf.mtype=(long)child_pid;
	msg_buf.mtext=recv_data;


	msgsnd(msgid,&msg_buf,sizeof(struct revc_to_opt),0);
	perror("msgsnd 处理进程");	
	
	

}
//将请求登陆的用户添加进用户列表
/*	功能：将请求登陆的用户添加到用户列表中
 *	参数：
 *		用户登陆表的地址
 *		用户的ip地址
 *		为用户创建的处理子进程的进程id号
 *		还未被使用的用户登陆表中的下表
 *	返回值：无
 * */
void add_cli_to_login_table(struct cli_to_login *table,in_addr_t ip,pid_t join_pid,int index)
{
	table[index].cli_addr=ip;
	table[index].child_pid=join_pid;
}
//处理进程查询登陆列表中客户端是否已经登陆
/*	功能：处理进程查询登陆列表中客户端是否已经登陆
 *	参数：
 *		用户登陆表的地址
 *		用户的ip地址 
 *		带回下标的空间地址
 *	返回值：存在：返回用户表中用户的下标 不存在返回 空的数组下标。
 * */
int isCliLogin(struct cli_to_login *table,in_addr_t ip,int *index)
{
	int i;
	for(i=0;i<10;i++)
	{
		if(ip==(in_addr_t)table[i].cli_addr)
		{	
			*index=i;
			return EXIST;
		}
	}
	for(i=0;i<10;i++)
	{
		if(table[i].cli_addr==-1)
		{
			*index=i;
			return NOEXIST;
		}
	}
	
	return FULL;
}
//处理子进程需要执行的命令函数集合
void *ser_login(void *arg,int sockfd)
{	
	struct spwd *naps=NULL;
	char *mpasswd=NULL;
	struct sockaddr_in addr;
	struct passwd *workhome;
	struct revc_to_opt *ldata=(struct revc_to_opt *)arg;

	struct ser_to_cli ser_result;
	bzero(&ser_result, sizeof(ser_result));
	naps=getspnam(ldata->data.ftp_arg.arglogin.login_user);
	if(naps==NULL)
	{
		ser_result.result=FAIL;	
	}
	addr=ldata->cli_addr;
	mpasswd=crypt(ldata->data.ftp_arg.arglogin.login_passwd,naps->sp_pwdp);
	if(!strcmp(mpasswd,naps->sp_pwdp))
	{
		ser_result.result=SUCCESS;
		workhome=getpwnam(ldata->data.ftp_arg.arglogin.login_user);
		strcpy(Work_home,workhome->pw_dir);
		chdir(Work_home);
	}else
	{
		ser_result.result=FAIL;
	}
	sendto(sockfd,&ser_result,sizeof(ser_result),0,(struct sockaddr *)&addr,sizeof(addr));
	perror("sendto");
	

	return NULL;
}
void *ser_pwd(void *arg,int sockfd)
{	
	struct sockaddr_in addr;
	struct ser_to_cli ser_result;
	struct revc_to_opt *ldata=(struct revc_to_opt *)arg;

	bzero(&ser_result, sizeof(ser_result));
	addr=ldata->cli_addr;
	ser_result.result=SUCCESS;
	strcpy(ser_result.string_result,Work_home);

	sendto(sockfd,&ser_result,sizeof(ser_result),0,(struct sockaddr *)&addr,sizeof(addr));
	perror("sendto");
}
//**********************CD函数()******************************************/

int cmd_cd_dir(char *dir)
{	
	int ret,i,len,num=0,index;
	char buf[1024];
	ret=strcmp(".",dir);
	if(ret==0)
	{
		chdir(dir);
		return SUCCESS;
	}
	ret=strcmp("..",dir);
	if(ret==0)
	{
		ret=chdir(dir);
		if(ret==-1)
		{
			return FAIL;
		}else
		{	
			strcpy(buf,Work_home);
			len=strlen(buf);
			for(i=0;i<len;i++)
			{
				if(buf[i]=='/')
				{
					num++;
					index=i;
				}
			}
			if(num==1)
			{
				strcpy(Work_home,"/");
			}else
			{
				buf[index]='\0';
				strcpy(Work_home,buf);
				return SUCCESS;
			}
		}
	}
	ret=strncmp(dir,"/",1);
	if(ret==0)
	{
		ret=chdir(dir);
		if(ret==0)
		{
			strcpy(Work_home,dir);

			return SUCCESS;
		}else
		{
			return FAIL;
		}
	}else
	{
		ret=chdir(dir);
		if(ret==0)
		{
			strcat(Work_home,dir);
			return SUCCESS;
		}else
		{
			return FAIL;
		}
	}
	return FAIL;
}
void *ser_cd(void *arg,int sockfd)
{	
	struct sockaddr_in addr;
	struct ser_to_cli ser_result;
	struct revc_to_opt *ldata=(struct revc_to_opt *)arg;
	bzero(&ser_result, sizeof(ser_result));
	int ret;
	ldata->data.ftp_arg.argCd;//  参数
	char dir[1024];
	addr=ldata->cli_addr;
	strcpy(dir,ldata->data.ftp_arg.argCd);
	ret=cmd_cd_dir(dir);

	ser_result.result=ret;
	sendto(sockfd,&ser_result,sizeof(ser_result),0,(struct sockaddr *)&addr,sizeof(addr));
	perror("sendto");
	printf("%s\n", __FUNCTION__);
}
char *gid_to_name(gid_t gid)
{
	struct group *grp_ptr;
	static char numstr[10];
	if((grp_ptr=getgrgid(gid))==NULL)
	{
		sprintf(numstr,"%d",gid);
		return numstr;
	}else
	{
		return grp_ptr->gr_name;
	}
}
char *uid_to_name(uid_t uid)
{
	struct passwd *pw_ptr;
	static char numstr[10];
	if((pw_ptr=getpwuid(uid))==NULL)
	{
		sprintf(numstr,"%d",uid);
		return numstr;
	}else
	{
		return pw_ptr->pw_name;
	}
}
char *ls_cmd(char *path)
{
	struct stat buf;
	char str[11];
	
	char time[128];
	mode_t mode;
	char *ptr=malloc(512);
	bzero(ptr,512);
	strcpy(str,"----------");
	lstat(path,&buf);
	mode=buf.st_mode;
	if(S_ISDIR(mode))   str[0]='d';
	if(S_ISCHR(mode))   str[0]='c';
	if(S_ISBLK(mode))   str[0]='b';
	if(S_ISFIFO(mode))  str[0]='p';
	if(S_ISLNK(mode))   str[0]='l';
	if(S_ISSOCK(mode))  str[0]='s';

	if(mode & S_IRUSR)  str[1]='r';
	if(mode & S_IWUSR)  str[2]='w';
	if(mode & S_IXUSR)  str[3]='x';
	if(mode & S_IRGRP)  str[4]='r';
	if(mode & S_IWGRP)  str[5]='w';
	if(mode & S_IXGRP)  str[6]='x';
	if(mode & S_IROTH)  str[7]='r';
	if(mode & S_IWOTH)  str[8]='w';
	if(mode & S_IXOTH)  str[9]='x';

	strcpy(ptr,str);
	strcat(ptr,"    ");
	strcat(ptr,uid_to_name(buf.st_uid));
	strcat(ptr,"    ");
	strcat(ptr,gid_to_name(buf.st_gid));
	strcat(ptr,"    ");
	strcpy(time,ctime(&(buf.st_ctime)));
	time[strlen(time)-1]='\0';
	strcat(ptr,time);
	strcat(ptr,"    ");
	strcat(ptr,path);
	printf("%s\n",ptr);
	return ptr;

}
void *ser_ls(void *arg,int sockfd)
{	
	DIR *dir;
	struct dirent * dir_name;
	struct ser_to_cli ser_result;
	struct sockaddr_in addr;
	char *ls;
	struct revc_to_opt *ldata=(struct revc_to_opt *)arg;
	
	addr=ldata->cli_addr;
	dir=opendir(".");
	if(dir==NULL)
	{
		perror("opendir");
		ser_result.result=FAIL;
		goto ERR;
	}
	while((dir_name=readdir(dir))!=NULL)
	{	
		bzero(&ser_result,sizeof(struct ser_to_cli));
		ser_result.result=SUCCESS;
		ls=ls_cmd(dir_name->d_name);
		strcpy(ser_result.string_result,ls);
		sendto(sockfd,&ser_result,sizeof(ser_result),0,(struct sockaddr *)&addr,sizeof(addr));
	}
	ser_result.result=LS_OVER;	
ERR:
	sendto(sockfd,&ser_result,sizeof(ser_result),0,(struct sockaddr *)&addr,sizeof(addr));
	perror("sendto");

	printf("%s\n", __FUNCTION__);
}
void *ser_get(void *arg,int sockfd)
{	
	struct ser_to_cli ser_result;
	struct sockaddr_in addr;
	struct revc_to_opt *ldata=(struct revc_to_opt *)arg;
	addr=ldata->cli_addr;
	addr.sin_port=htons(TCP_PORT);
	bzero(&ser_result, sizeof(ser_result));

	int sock_tcp,ret;
	int fd,num;
	int i=1;
	char buf[BUFSIZ];
	fd=open(ldata->data.ftp_arg.argGet,O_RDONLY);
	if(fd<0)
	{
		perror("open");
		goto ERRL;
	}
	sock_tcp=socket(AF_INET,SOCK_STREAM,0);
	if(sock_tcp<0)
	{
		perror("sockfd");
		goto ERRFD;
	}
	ret=connect(sock_tcp,(struct sockaddr *)&addr,sizeof(struct sockaddr_in));
	if(ret<0)
	{
		perror("connect");
		goto ERR;
	}
	while(1)
	{
		num=read(fd,buf,sizeof(buf));
		write(sock_tcp,buf,num);
		if(num==0) break;
	}
	i=0;
	addr=ldata->cli_addr;
	
ERR:
	close(sock_tcp);
ERRFD:
	close(fd);
ERRL:
	if(i==1)
	{
		ser_result.result=FAIL;
	}else
	{
		ser_result.result=SUCCESS;
	}
	sendto(sockfd,&ser_result,sizeof(ser_result),0,(struct sockaddr *)&addr,sizeof(addr));
	perror("sendto");
	printf("%s\n", __FUNCTION__);
}
void *ser_put(void *arg,int sockfd)
{
	int i=1;
	int sock_tcp,confd;
	int addrlen_tcp=sizeof(struct sockaddr_in);
	int ret,num;
	char buf[BUFSIZ];
	
	struct sockaddr_in tcp_addr;
	struct ser_to_cli ser_result;
	struct revc_to_opt *ldata=(struct revc_to_opt *)arg;
	
//	addr.sin_port=htons(TCP_PUT);
	bzero(&ser_result, sizeof(ser_result));
	printf("进入put函数\n");
//	tcp_addr=addr;
//	tcp_addr.sin_port=htons(TCP_PUT);
//***************tcp网络**********************************//	
	sock_tcp=socket(AF_INET,SOCK_STREAM,0);
	if(sock_tcp<0)
	{
		perror("socket");
		printf("下载失败！\n");
		return;
	}
	bzero(&tcp_addr,sizeof(tcp_addr));
	tcp_addr.sin_family=AF_INET;
	tcp_addr.sin_port=htons(TCP_PUT);
	tcp_addr.sin_addr.s_addr=htonl(INADDR_ANY);

	ret=bind(sock_tcp,(struct sockaddr *)&tcp_addr,sizeof(struct sockaddr_in));
	if(ret<0)
	{
		perror("bind sock_tcp");
		printf("上传失败！\n");
		close(sock_tcp);
		return;
	}

	struct sockaddr_in addr;
	addr=ldata->cli_addr;
	ser_result.result=666;
	sendto(sockfd,&ser_result,sizeof(struct ser_to_cli),0,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
	ret=listen(sock_tcp,5);
	if(ret<0)
	{
		perror("listen");
		printf("上传失败！\n");
		close(sock_tcp);
		return;
	}
	bzero(&tcp_addr,sizeof(tcp_addr));
	confd=accept(sock_tcp,(struct sockaddr *)&tcp_addr,&addrlen_tcp);
	if(confd<0)
	{
		perror("accept");
		printf("上传失败！\n");
		close(sock_tcp);
		return;
	}
	//***************************************************//
	printf("网络连接成功准备打开文件\n");
	int fd;
	fd=open(ldata->data.ftp_arg.argPut,O_CREAT|O_WRONLY);
	if(fd<0)
	{
		perror("open");
		printf("上传失败！\n");
		close(confd);
		close(sock_tcp);
		return;
	}
	printf("打开文件成功准备读写文件");
	while(1)
	{
		num=read(confd,buf,sizeof(buf));
		write(fd,buf,sizeof(buf));
		if(num==0) break;
	}
	i=0;
	close(fd);
	close(sock_tcp);
	if(i==0)
	{
		ser_result.result=SUCCESS;
	}else
	{
		ser_result.result=FAIL;
	}
	sendto(sockfd,&ser_result,sizeof(struct ser_to_cli),0,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
	
}
void *ser_quit(void *arg,int sockfd)
{	
	//删除对应客户端在登陆列表中的信息
	struct ser_to_cli ser_result;
	struct sockaddr_in addr;
	struct cli_to_login *table;
	struct revc_to_opt *ldata=(struct revc_to_opt *)arg;
	addr=ldata->cli_addr;
	bzero(&ser_result, sizeof(ser_result));
	//打开共享内存
	int shmfd;
	shmfd=shmget(SHMNAME,0,O_RDWR);
	if(shmfd<0)
	{
		perror("shmget quit");
		ser_result.result=FAIL;
		goto ERR;

	}
	//映射共享内存
	table=shmat(shmfd,NULL,0);
	if(table==(void*)-1)
	{
		perror("shmat");
		ser_result.result=FAIL;
		goto ERR;
	}
	int ret;
	int cli_index=-1;
	ret=isCliLogin(table,ldata->cli_addr.sin_addr.s_addr,&cli_index);
	if(ret==EXIST)
	{
		table[cli_index].cli_addr=-1;
		table[cli_index].child_pid=-1;
		ser_result.result=SUCCESS;
	}else
	{
		ser_result.result=FAIL;
	}

	printf("%s\n", __FUNCTION__);
ERR:
	sendto(sockfd,&ser_result,sizeof(ser_result),0,(struct sockaddr *)&addr,sizeof(addr));
	perror("quit sendto");

	shmdt(table);
	if(ser_result.result==SUCCESS)
	{
		exit(CMD_QUIT);
	}else
	{
		exit(FAIL);
	}
}
//*******************************************************************//
//接受进程
//*******************************************************************//
void recvProc_fun(int sockfd,int pipeWr)
{	
	struct sockaddr_in *pcli_addr;
	struct cli_to_ser buf;
	struct revc_to_opt recvProc2OptProc;
	while(1)
	{
		//从网络中接受数据
		pcli_addr=recv_data_from_cli(sockfd, &buf);
		if(pcli_addr==NULL) continue;
		//将数据打包（接受的数据和客户端的ip地址）
		recvProc2OptProc.data=buf;
		recvProc2OptProc.cli_addr=*pcli_addr;

		//将数据从管道中发送给处理进程
		recvProc_send_optProc(&recvProc2OptProc,pipeWr);
	}
}
//服务器从网络中接受客户端传递的消息
struct sockaddr_in *recv_data_from_cli(int sockfd,struct cli_to_ser *buf)
{
	struct sockaddr_in cli_addr;
	int addrlen=sizeof(struct sockaddr_in);
	int ret;

	struct sockaddr_in *p=malloc(sizeof(struct sockaddr_in));

	bzero(&cli_addr,sizeof(cli_addr));
	ret=recvfrom(sockfd,buf,sizeof(struct cli_to_ser),0,(struct sockaddr*)&cli_addr,&addrlen);
	if(ret<0)
	{
		perror("recvfrom");
		return NULL;
	}
	*p=cli_addr;
	return p;
}
//接受进程将消息写入管道
void recvProc_send_optProc(struct revc_to_opt *recvProc2OptProc,int pipe)
{
	write(pipe,recvProc2OptProc,sizeof(struct revc_to_opt));
}
//********************************************************************//
